package com.chisheng.caa.service.impl;

import cn.hutool.core.util.URLUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.chisheng.caa.exception.IllegalArgumentException;
import com.chisheng.caa.mapper.CourseMapper;
import com.chisheng.caa.mapper.MajorMapper;
import com.chisheng.caa.pojo.ImportCourseSetting;
import com.chisheng.caa.pojo.dto.CourseDTO;
import com.chisheng.caa.pojo.dto.ExcelSheetDTO;
import com.chisheng.caa.pojo.dto.GradeDTO;
import com.chisheng.caa.pojo.entity.Course;
import com.chisheng.caa.service.AbstractImportCourseService;
import com.chisheng.caa.service.ExcelImportCourseService;
import com.chisheng.caa.util.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ImportCourseServiceImpl extends AbstractImportCourseService implements ExcelImportCourseService {

    @Resource
    private ValidationUtil validationUtil;

    @Resource
    private ImportCourseSetting defaultSetting;

    @Resource
    private MajorMapper majorMapper;

    @Resource
    private CourseMapper courseMapper;

    @Autowired
    public ImportCourseServiceImpl(ExpiryMapCacheUtil cacheUtil) {
        super(cacheUtil);
    }

    @Override
    public String open() {
        ExcelImportCourseState state = new ExcelImportCourseState();
        state.setSetting(defaultSetting);
        state.setData(Map.of());
        return cacheUtil.put(state);
    }

    @Override
    public void load(String accessKey, URL url) {
        ExcelImportCourseState state = cacheUtil.checkAndGet(accessKey);
        // 数据读取
        List<ReadSheet> sheetList = EasyExcelUtil.getSheetList(URLUtil.getStream(url));
        ReadSheet currentSheet = Objects.requireNonNull(sheetList.get(0), "工作表不能为空");
        Collection<CourseDTO> courseList = readExcelFile(URLUtil.getStream(url), currentSheet.getSheetNo());
        // 状态更新
        state.setUrl(url);
        state.setCurrentSheetNo(currentSheet.getSheetNo());
        state.setSheetList(sheetList);
        updateCourseMap(state, courseList);
    }

    @Override
    public List<ExcelSheetDTO> getSheetList(String accessKey) {
        ExcelImportCourseState state = cacheUtil.checkAndGet(accessKey);
        checkExcelFileIsLoaded(state);
        return state.getSheetList().stream().map(source -> {
            ExcelSheetDTO dest = BeanUtil.copyProperties(source, ExcelSheetDTO.class);
            dest.setActive(Objects.equals(dest.getSheetNo(), state.getCurrentSheetNo()));
            return dest;
        }).collect(Collectors.toList());
    }

    @Override
    public void switchSheet(String accessKey, Integer sheetNo) {
        ExcelImportCourseState state = cacheUtil.checkAndGet(accessKey);
        checkExcelFileIsLoaded(state);
        if (Objects.equals(sheetNo, state.currentSheetNo)) {
            return;
        }
        if (state.getSheetList().stream().noneMatch(sheet -> Objects.equals(sheet.getSheetNo(), sheetNo))) {
            throw new IllegalArgumentException(String.format("不存在的工作表下标 '%d'", sheetNo));
        }

        // 数据读取
        Collection<CourseDTO> courseList = readExcelFile(URLUtil.getStream(state.getUrl()), sheetNo);
        // 状态更新
        updateCourseMap(state, courseList);
        state.setCurrentSheetNo(sheetNo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void write(String accessKey) {
        ExcelImportCourseState state = cacheUtil.checkAndGet(accessKey);
        GradeDTO grade = state.getSetting().getGrade();
        if (grade.isEmpty()) {
            throw new IllegalArgumentException("导入年级不能为空");
        }

        String majorId = majorMapper.selectIdByMajorName(grade.getMajorName());
        state.getData().values().forEach(source -> {
            Course dest = new Course();
            BeanUtil.copyProperties(source, dest);
            dest.setMajorId(majorId);
            dest.setEnrollmentYear(grade.getEnrollmentYear());
            courseMapper.insert(dest);
        });

        cacheUtil.remove(accessKey);
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public final static class ExcelImportCourseState extends ImportCourseState {

        /**
         * Excel 文件 URL
         */
        private URL url;

        /**
         * 工作表列表
         */
        private List<ReadSheet> sheetList;

        /**
         * 当前的工作表
         */
        private Integer currentSheetNo;

    }

    /**
     * 读取 Excel 文件中的课程
     */
    private Collection<CourseDTO> readExcelFile(InputStream is, Integer sheetNo) {
        ArrayList<CourseDTO> courseList = new ArrayList<>();
        EasyExcel.read(is, CourseDTO.class, new AnalysisEventListener<CourseDTO>() {
            @Override
            public void invoke(CourseDTO data, AnalysisContext context) {
                validationUtil.validate(data);
                courseList.add(data);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
            }
        }).sheet(sheetNo).headRowNumber(1).doRead();

        // 自动填充课程性质、课程类型列
        Optional<CourseDTO> course = courseList
                .stream()
                .filter(c -> c.getCourseNature() != null)
                .findFirst();

        if (course.isPresent()) {
            String courseType = course.get().getCourseNature();
            courseList.forEach(c -> {
                if (c.getCourseNature() == null) {
                    c.setCourseNature(courseType);
                }
            });
        }

        return courseList;
    }

    /**
     * 检查是否读取了 Excel 文件
     */
    private void checkExcelFileIsLoaded(ExcelImportCourseState state) {
        Assert.isTrue(state.getUrl() != null, "尚未加载 Excel 文件");
    }

    /**
     * 更新 courseMap 数据
     */
    private void updateCourseMap(ExcelImportCourseState state, Collection<CourseDTO> courseList) {
        ImportCourseSetting setting = state.getSetting();
        System.out.println(setting);
        MapUtil.putAll(state.getData(), courseList,
                setting.getLoadingStrategy(), setting.getDuplicateStrategy());
    }

}
